from turtle import Turtle, Screen, mainloop, Vec2D
import math
import random

"""
Ref:
    * The Nature of Code: NOC_1_10_motion_101_acceleration
"""

class PVec2D(Vec2D):
    def setMag(self, val):
        v = math.sqrt(self[0]**2 + self[1]**2)
        v0 = val/(v+0.001)*self[0]
        v1 = val/(v+0.001)*self[1]
        return PVec2D(v0, v1)

    def limit(self, val):
        v = math.sqrt(self[0]**2 + self[1]**2)
        v0 = val/(v+0.001)*self[0]
        v1 = val/(v+0.001)*self[1]
        return PVec2D(v0, v1)

    def add(self, vec):
        self[0] = self[0] + vec[0]
        self[1] = self[1] + vec[1]

    def __add__(self, other):
        return PVec2D(self[0]+other[0], self[1]+other[1])

    def __sub__(self, other):
        return PVec2D(self[0]-other[0], self[1]-other[1])

class TurtleInteractive(Turtle):
    MOVING, DRAGGING = range(2)  # states

    def __init__(self, screen):
        Turtle.__init__(self)

        self.screen = screen
        self.state = self.MOVING

        # Initially we track the turtle's motion and left button clicks
        self.onmove(self.screen, self.move_handler)  # a la screen.onmove(move_handler)
        self.onclick(self.click_handler)  # a click will turn motion into drag

    def handle_move(self, x, y):
        self.penup()
        self.setheading(self.towards(x, y))
        self.goto(x, y)

    def handle_click(self, x, y):
        pass

    def handle_release(self, x, y):
        pass

    def handle_drag(self, x, y):
        self.pendown()
        self.setheading(self.towards(x, y))
        self.goto(x, y)


    def move_handler(self, x, y):
        if self.state != self.MOVING:  # ignore stray events
            return

        self.onmove(self.screen, None)  # avoid overlapping events
        self.handle_move(x, y)
        self.onmove(self.screen, self.move_handler)

    def click_handler(self, x, y):
        self.onclick(None)  # disable until release
        self.onmove(self.screen, None)  # disable competing handler

        self.onrelease(self.release_handler)  # watch for release event
        self.ondrag(self.drag_handler)  # motion is now dragging until release

        self.handle_click(x, y)

        self.state = self.DRAGGING

    def release_handler(self, x, y):
        self.onrelease(None)  # disable until click
        self.ondrag(None)  # disable competing handler

        self.onclick(self.click_handler)  # watch for click event
        self.onmove(self.screen, self.move_handler)  # dragging is now motion until click

        self.handle_release(x, y)

        self.state = self.MOVING

    def drag_handler(self, x, y):
        if self.state != self.DRAGGING:  # ignore stray events
            return

        self.ondrag(None)  # disable event inside event handler
        self.handle_drag(x, y)
        self.ondrag(self.drag_handler)  # reenable event on event handler exit

    def onmove(self, obj, fun, add=None):
        """
        Bind fun to mouse-motion event on screen.

        Arguments:
        self -- the singular screen instance
        fun  -- a function with two arguments, the coordinates
            of the mouse cursor on the canvas.
        """

        if fun is None:
            obj.cv.unbind('<Motion>')
        else:
            def eventfun(event):
                fun(obj.cv.canvasx(event.x) / obj.xscale, -obj.cv.canvasy(event.y) / obj.yscale)
            obj.cv.bind('<Motion>', eventfun, add)


class Mover(TurtleInteractive):
    def __init__(self, screen):
        TurtleInteractive.__init__(self, screen)

        self.shape("circle")
        self.shapesize(2)

        cl = int(random.random()*127)

        self.color((0, 0, 0), (cl, cl, cl))

        self.scree_size = screen.screensize()

        self.position_  = PVec2D(self.scree_size[0]*random.random(),
                                 self.scree_size[1]*random.random())
        self.mouse_pos_ = PVec2D(0, 0)

        self.velocity_ = PVec2D(0, 0)
        self.topspeed_ = 5

        self.penup()
        self.setpos(self.position_[0], self.position_[1])

    def handle_move(self, x, y):
        self.mouse_pos_ = PVec2D(x, y)

    def handle_timer(self):
        acc = self.mouse_pos_ - self.position_
        acc = acc.setMag(1)

        self.velocity_ = self.velocity_ + acc
        self.velocity_ = self.velocity_.limit(self.topspeed_)

        self.position_ = self.position_ + self.velocity_
        self.setpos(self.position_[0], self.position_[1])

def draw():
    screen = Screen()
    screen.setup(600, 400)

    screen.colormode(255)

    mover = Mover(screen)
    mover.speed('fastest')


    def handle_timer():
        mover.handle_timer()
        screen.ontimer(handle_timer, 1)

    screen.ontimer(handle_timer, 1)

if __name__ == "__main__":
    draw()

    mainloop()
